#region File Description
//-----------------------------------------------------------------------------
// ParticleEmitter.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Stonecode.Solar.ParticleEngine.ParticleSystems;
using Stonecode.Solar.ParticleEngine.Emitters;
using Microsoft.Xna.Framework.Graphics;
using Stonecode.Solar.ParticleEngine.Utils;
#endregion

namespace Stonecode.Solar.ParticleEngine
{
    
    public class PointEmitter : Emitter
    {
        #region Fields
        //protected Vector3 _position;
        protected Vector3 previousPosition;
        //public EmitterTypeSettings EmitterTypeSettings = new EmitterTypeSettings(EmitterType.Point);
        public PointEmitterTypeSettings EmitterTypeSettings { get; set; }

        private Vector2 _velDirection, _vectorZero;
        private float _distance, ellipseHeight, ellipseWidth, _direction;
        #endregion


        /// <summary>
        /// Constructs a new particle emitter object.
        /// </summary>
        public PointEmitter(GraphicsDevice graphics, Effect pEffect, EmitterSettings psettings,
            Texture2D texture, float particlesPerSecond, Vector3 initialPosition,
            Matrix view, Matrix proj)
            : base(graphics, pEffect, psettings, texture, particlesPerSecond, view, proj, EmitterType.Point)
        {
            //timeBetweenParticles = 1.0f / particlesPerSecond;
            previousPosition = initialPosition;
            _vectorZero = Vector2.Zero;
            EmitterTypeSettings = new PointEmitterTypeSettings();
        }

        public PointEmitter(GraphicsDevice graphics, Effect pEffect, EmitterSettings psettings,
            PointEmitterTypeSettings pets,
            Texture2D texture, float particlesPerSecond, Vector3 initialPosition,
            Matrix view, Matrix proj)
            : base(graphics, pEffect, psettings, texture, particlesPerSecond, view, proj, EmitterType.Point)
        {
            previousPosition = initialPosition;
            _vectorZero = Vector2.Zero;
            EmitterTypeSettings = pets;
        }

        public override void Dispose()
        {
            EmitterTypeSettings = null;
            base.Dispose();
        }

        private void GetRandomVelocity(out Vector2 v)
        {
            NumberGenerator.GenerateRandomVector2(_particleSystem.Settings.MinVelocity, _particleSystem.Settings.MaxVelocity, out v);
            if (!EmitterTypeSettings.EllipticalVelocity)
                return;

            ellipseHeight = Math.Abs(_particleSystem.Settings.MaxVerticalVelocity - _particleSystem.Settings.MinVerticalVelocity);
            ellipseWidth = Math.Abs(_particleSystem.Settings.MaxHorizontalVelocity - _particleSystem.Settings.MinHorizontalVelocity);

            Vector2.Normalize(ref v, out _velDirection);
            Vector2.Distance(ref v, ref _vectorZero, out _distance);

            _distance -= (_distance - (Math.Min(ellipseHeight, ellipseWidth) / 2f));
            if (_distance <= 0)
                return;

            Vector2.Multiply(ref _velDirection, _distance, out v);

            // if the ellipse is actually a circle, then there is nothing else to do...
            if (ellipseWidth == ellipseHeight)
                return;

            
            // Stretch the vector in the direction of the ellipse log side
            if (ellipseWidth < ellipseHeight)
            {
                v.X += (_particleSystem.Settings.MaxHorizontalVelocity + _particleSystem.Settings.MinHorizontalVelocity) / 2f;
                v.Y += (((ellipseHeight - ellipseWidth) / 2f) * _velDirection.Y);
            }
            else
            {
                v.X += (((ellipseWidth - ellipseHeight) / 2f) * _velDirection.X);
                v.X += (_particleSystem.Settings.MaxVerticalVelocity + _particleSystem.Settings.MinVerticalVelocity) / 2f;
            }
            
            
        }

        /// <summary>
        /// Updates the emitter, creating the appropriate number of particles
        /// in the appropriate positions.
        /// </summary>
        public override void Update(float dt)
        {
            if (IsAlive && Enabled && AutoGenerate)
            {
                // Work out how much time has passed since the previous update.
                //float elapsedTime = dt; //(float)gameTime.ElapsedGameTime.TotalSeconds;

                if (dt > 0)
                {
                    // Work out how fast we are moving.
                    

                    // If we had any time left over that we didn't use during the
                    // previous update, add that to the current elapsed time.
                    float timeToSpend = timeLeftOver + dt;

                    // Counter for looping over the time interval.
                    float currentTime = -timeLeftOver;

                    // Create particles as long as we have a big enough time interval.
                    while (timeToSpend > timeBetweenParticles)
                    {
                        currentTime += timeBetweenParticles;
                        timeToSpend -= timeBetweenParticles;

                        // Work out the optimal position for this particle. This will produce
                        // evenly spaced particles regardless of the object speed, particle
                        // creation frequency, or game update rate.
                        float mu = currentTime / dt;
                        Vector2 velocity; //(_position - previousPosition) / elapsedTime;
                        Vector3 position;

                        GetRandomVelocity(out velocity);

                        if (dt >= .5f)
                            position = Vector3.Lerp(previousPosition, base.Position, mu);
                        else
                            position = base.Position;

                        // Create the particle.
                        _particleSystem.AddParticleManualVelocity(position, new Vector3(velocity, 0f));
                    }

                    // Store any time we didn't use, so it can be part of the next update.
                    timeLeftOver = timeToSpend;
                }

                previousPosition = base.Position;
            }

           
            base.Update(dt);

        }

        public override void Trigger(float dt)
        {
            float timeToSpend = timeLeftOver + dt;

            // Counter for looping over the time interval.
            float currentTime = -timeLeftOver;

            // Create particles as long as we have a big enough time interval.
            while (timeToSpend > timeBetweenParticles)
            {
                currentTime += timeBetweenParticles;
                timeToSpend -= timeBetweenParticles;

                Vector2 velocity;
                GetRandomVelocity(out velocity);
                
                //GetPointOnLine();

                // Create the particle.
                _particleSystem.AddParticleManualVelocity(base.Position, new Vector3(velocity, 0));
            }

            timeLeftOver = timeToSpend;
        }

        public override void Trigger(int particleCount)
        {
            for (int i = 0; i < particleCount; i++)
            {
                Vector2 velocity;
                GetRandomVelocity(out velocity);
                //GetPointOnLine();

                // Create the particle.
                _particleSystem.AddParticleManualVelocity(base.Position, new Vector3(velocity, 0));
            }
        }

        public override void TriggerWithOffset(float dt, Vector2 offset)
        {
            float timeToSpend = timeLeftOver + dt;

            // Counter for looping over the time interval.
            float currentTime = -timeLeftOver;

            // Create particles as long as we have a big enough time interval.
            while (timeToSpend > timeBetweenParticles)
            {
                currentTime += timeBetweenParticles;
                timeToSpend -= timeBetweenParticles;

                Vector2 position = new Vector2(base.Position.X, base.Position.Y);
                Vector2 velocity;
                GetRandomVelocity(out velocity);
                Vector2.Add(ref position, ref offset, out position);
                //GetPointOnLine();

                // Create the particle.
                _particleSystem.AddParticleManualVelocity(new Vector3(position, Position.Z), new Vector3(velocity, 0));
            }

            timeLeftOver = timeToSpend;

        }


    }
}
